﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lesson3
{

    #region 枚举
    // 枚举
    // 它是一个被命名的整形常量的集合
    // 一般用来表示 状态、类型 等
    // 声明枚举：相当于是创建一个自定义的枚举类型
    // 声明枚举变量： 使用声明的自定义枚举类型 创建一个枚举变量
    // 语法  枚举以 E 或者 E_开头，作为我们的命名规范
    // enum E_自定义的枚举名
    // {
    //      自定义枚举项名字,  // 0   枚举中包裹的 整形常量 第一个默认值是0 往下会依次累加
    //      自定义枚举项名字,  // 1
    //      自定义枚举项名字   // 2
    // }

    // enum E_自定义的枚举名
    // {
    //      自定义枚举项名字 = 5,  // 也可以给枚举项赋值, 赋完值后，第一个枚举项默认值变成5了，下面的会依次累加
    //      自定义枚举项名字,      // 6
    //      自定义枚举项名字,      // 7
    //      自定义枚举项名字 = 100,   
    //      自定义枚举项名字         // 101   这个会在100后面累加
    // }

    // 枚举通常都在namespace语句块中声明
    // 有时也会在 class语句块 或者 struct(结构体)语句块中
    // 注意：枚举不能在函数语句块中声明

    enum E_MonsterType
    {
        Normal,  // 0

        Boss,   // 1
    }

    enum E_PlayerType
    {
        Main = 100,
        Other,      // 101
        NPC,
    }
    #endregion

    #region 结构体
    // 结构体是一种自定义的变量类型
    // 它是数据和函数的集合
    // 在结构体中 可以声明各种变量和方法

    // 作用：用来表现存在关系的数据集合 比如：人、动物、车、房子等

    // 结构体一般写在namespace语句块中
    // 结构体关键字： struct
    // struct 声明
    //struct 自定义结构体名
    //{
    //    // 变量
    //    // 构造函数 （可选）
    //    // 函数（方法）
    //}
    // 结构体名字 通常也是用 帕斯卡命名法

    // 实例
    // 定义 学生数据的 结构体
    struct Student
    {
        // 变量
        // 结构体中声明的变量 不能直接初始化
        // 变量类型 可以是任意的数据类型 包括结构体 但不能是自己
        public string name;
        public int age;
        public bool sex;

        // 构造函数
        // 概念
        //  - 没有返回值
        //  - 函数名必须和结构体名一样
        //  - 必须有参数
        //  - 如果声明了构造函数，那么必须在其中对所有变量数据进行初始化
        public Student(string name, int age, bool sex)
        {
            // 需用this关键字来访问结构体中的变量，区分传递的参数名
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        // 还可以默认赋值
         public Student(string name)
        {
            this.name = name;
            // 必须对所有变量进行初始化，否则会报错
            age = 19;         
            sex = false;
        }
        // 结构体中的构造函数 可以有多个
        // 但是参数列表必须不一样
        // 结构体中始终会有一个默认的无参构造函数

        // 函数方法
        // 表现这个数据结构的行为
        // 在构造体中的方法 目前不需要加 static 关键字
        public void Study()
        {
            // 在结构体中的函数（方法）中 可以直接访问、使用结构体中声明的变量
            Console.WriteLine("我的名字是{0}，我今年{1}岁", name, age);
            Console.WriteLine("学习");
        }

        // 访问修饰符
        // 修饰结构体中的变量、函数（方法） 是否能被外部访问
        // public  公共的 任何地方都可以访问
        // private 私有的 只能在结构体内部访问
        // 默认访问修饰符是 private
        
    }

    #endregion

    internal class Program
    {
        static void Main(string[] args)
        {
            // c#复杂数据类型
            // 复杂数据类型特点
            // 1 数据集合   一般是多个数据（变量）集合在一起构成的
            // 2 自定义  一般可以自己取名字，可以自定义的数据（变量） 
            // 枚举： 整形常量的集合，可以自定义
            // 数组： 任意变量类型顺序存储的数据
            // 结构体： 任意变量的数据集合，可以自定义

            #region 枚举的使用
            // 枚举的使用，要在函数中
            // 声明枚举变量
            // 自定义的枚举类型 变量名 = 默认值; (自定义枚举类型.枚举项)
            E_PlayerType playerType = E_PlayerType.Main;  

            if(playerType == E_PlayerType.Main)
            {
                Console.WriteLine("写主玩家逻辑");
            }
            else if(playerType == E_PlayerType.Other)
            {
                Console.WriteLine("写其他玩家逻辑");
            }
            else
            {
                Console.WriteLine("写NPC的逻辑");
            }

            // 枚举和switch是天生一对
            E_MonsterType monsterType = E_MonsterType.Boss;
            // 当switch后面的变量写上monsterType并回车时，会自动补全成以下代码
            switch (monsterType)
            {
                case E_MonsterType.Normal:
                    break;
                case E_MonsterType.Boss:
                    break;
                default:
                    break;
            }

            // 枚举的类型转换
            // 和整形 int 转换
            int i = (int)playerType;
            Console.WriteLine(i);

            // int转枚举
            playerType = 0;

            // 一般很少转成整形进行计算

            // 和 string 字符串相互转换  也很少使用
            string str = playerType.ToString();
            Console.WriteLine(str);

            // string转枚举  Enum的Parse方法，第一个参数：要转的是哪个枚举类型，第二个参数：用于转换的对应枚举项的字符串
            // 转换完后，是一个Object的通用类型，没法隐式转换成枚举，所以要加强转
            playerType = (E_PlayerType)Enum.Parse(typeof(E_PlayerType), "Main");
            Console.WriteLine(playerType);  // 这里会默认调用ToString

            // 枚举的作用
            // 设置状态，有语义化的效果

            #endregion

            #region 数组
            // 数组是存储一组相同类型的集合
            // 数组分为一维、多维、交错数组

            // 数组的声明
            // 1  变量类型[] 数组名;   // 只是声明数组，没有赋值
            int[] arr;

            // 2  变量类型[] 数组名 = new 变量类型[数组长度];   // 声明数组，不初始化，会赋默认值，默认值是0
            int[] arr1 = new int[3];

            // 3  变量类型[] 数组名 = new 变量类型[数组长度]{内容1, 内容2, 内容3....};   // 声明数组，并赋初始值
            int[] arr2 = new int[3] { 1, 21, 3};   // 大括号里的内容数量必须和数组长度一样

            // 4  变量类型[] 数组名 = new 变量类型[]{内容1, 内容2, 内容3....};   // 声明数组，并赋初始值
            int[] arr3 = new int[] { 1, 2, 3, 4, 5, 6 };   // 没有指定长度，内容就没了限制

            // 5  变量类型[] 数组名 = {内容1, 内容2, 内容3....};  // 字面量式声明并赋值
            int[] arr4 = { 1, 2, 3, 4, 5, 6 };      // 内容长度决定数组长度


            // 数组的使用
            arr = arr3;
            // 数组长度： 数组变量名.Length
            Console.WriteLine(arr.Length);
            // 获取数组中的元素  不能越界
            Console.WriteLine(arr[0]);
            Console.WriteLine(arr[1]);
            Console.WriteLine(arr[arr.Length - 1]);

            // 修改数组中的元素
            arr[0] = 100;
            Console.WriteLine(arr[0]);

            // 遍历数组
            for (int j = 0; j < arr.Length; j++)
            {
                Console.WriteLine(arr[j]);
            }

            // 增加数组中的元素
            //  数组初始化以后 是不能够 直接添加新的元素的
            int[] array = new int[arr.Length + 5];    // 添加5个位置
            for (int j = 0; j < arr.Length; j++)
            {
                array[j] = arr[j];
            }
            arr = array;   // 为原数组增加新的位置
            arr[6] = 51;   // 为新位置赋值
            arr[7] = 52;
            arr[8] = 53;
            arr[9] = 54;
            arr[10] = 55;


            // 删除数组中的元素
            //  数组初始化以后 是不能够 直接删除元素的
            int[] array2 = new int[arr.Length - 5];    // 删除5个位置
            for (int j = 0; j < array2.Length; j++)
            {
                array2[j] = arr[j];
            }
            arr = array2;   // 将减少的数组再赋给原数组


            // 查找数组中的元素
            int a = 3;
            for (int j = 0;j < arr.Length; j++)
            {
                if(arr[j] == a)
                {
                    Console.WriteLine("和a相等的元素在下标为{0}的索引位置", j);
                    break;
                }
            }

            #endregion

            #region 二维数组
            // 二维数组 是使用两个下标（索引）来确定元素的数组
            // 两个下标可以理解成 行标 和 列标

            // 二维数组的声明
            // 变量类型[,] 变量名；
            int[,] d_arr;

            // 变量类型[,] 变量名  = new 变量类型[行, 列]
            int[,] d_arr2 = new int[3, 3];

            // 变量类型[,] 变量名  = new 变量类型[行, 列]{{内容1, 内容2, ...},{内容1, 内容2, ...},{内容1, 内容2, ...}}
            int[,] d_arr3 = new int[3, 3] { { 1, 2, 3 }, 
                                            { 4, 5, 6 }, 
                                            { 7, 8, 9 } };

            // 变量类型[,] 变量名  = new 变量类型[, ]{{内容1, 内容2, ...},{内容1, 内容2, ...},{内容1, 内容2, ...}}
            int[,] d_arr4 = new int[, ] { { 1, 2, 3 },
                                          { 4, 5, 6 } };

            // 变量类型[,] 变量名  = {{内容1, 内容2, ...},{内容1, 内容2, ...},{内容1, 内容2, ...}}
            int[,] d_arr5 = { { 1, 2, 3 }, { 4, 5, 6 } };

            // 二维数组的使用
            int[,] d_array = new int[,]{ { 5, 5, 9 },
                                         { 6, 8, 3 } };

            // 二维数组的长度
            // 我们要获取行和列分别是多长
            Console.WriteLine(d_array.GetLength(0));  // 得到多少行
            Console.WriteLine(d_array.GetLength(1));  // 得到多少列

            // 获取二维数组中的元素
            Console.WriteLine(d_array[0,1]);
            Console.WriteLine(d_array[1,1]);
            Console.WriteLine(d_array[d_array.GetLength(0) - 1, d_array.GetLength(1) - 1]);

            // 修改二维数组中的元素
            d_array[0, 0] = 101;
            Console.WriteLine(d_array[0, 0]);

            // 遍历二维数组
            for (int j = 0; j < d_array.GetLength(0); j++)
            {
                for (int k = 0; k < d_array.GetLength(1); k++)
                {
                    Console.WriteLine(d_array[j, k]);
                }
            }

            // 增加数组的元素
            int[,] d_arr_copy = new int[3,4];
            for (int j = 0; j < d_array.GetLength(0); j++)
            {
                for (int k = 0; k < d_array.GetLength(1); k++)
                {
                    d_arr_copy[j,k] = d_array[j,k];
                }
            }
            d_array = d_arr_copy;
            d_array[0, 3] = 33;
            d_array[1, 3] = 35;
            d_array[2, 0] = 55;
            d_array[2, 1] = 58;
            d_array[2, 2] = 59;
            d_array[2, 3] = 60;
            for (int j = 0; j < d_array.GetLength(0); j++)
            {
                for (int k = 0; k < d_array.GetLength(1); k++)
                {
                    Console.WriteLine(d_array[j, k]);
                }
            }

            // 删除数组的元素

            // 查找数组中的元素


            #endregion

            #region 交错数组
            // 交错数组是 数组的数组 每个维度的数量可以不同
            // 二维数组的每行的列数相同， 交错数组每行的列数可能不同

            // 声明
            // 变量类型[][] 交错数组名;
            int[][] j_arr;

            // 变量类型[][] 交错数组名 = new 变量类型[行数][];  // 列数就可以随意了
            int[][] j_arr2 = new int[3][];

            // 变量类型[][] 交错数组名 = new 变量类型[行数][]{一维数组1, 一维数组2, ...}; 
            int[][] j_arr3 = new int[3][] { new int[] { 1, 2, 3}, 
                                            new int[] { 1, 2},
                                            new int[] { 1 } };

            // 变量类型[][] 交错数组名 = new 变量类型[][]{一维数组1, 一维数组2, ...}; 
            int[][] j_arr4 = new int[][] { new int[] { 1, 2, 3},
                                            new int[] { 1, 2},
                                            new int[] { 1 } };

            // 变量类型[][] 交错数组名 = {一维数组1, 一维数组2, ...}; 
            int[][] j_arr5 = { new int[] { 1, 2, 3},
                                new int[] { 1, 2},
                                new int[] { 1 } };

            // 交错数组的使用
            int[][] j_array = new int[2][] { new int[] { 5, 6}, new int[] { 9, 3 } };

            // 数组长度
            // 行
            Console.WriteLine(j_array.GetLength(0));
            // 得到某一行的列数
            Console.WriteLine(j_array[0].Length);

            // 获取交错数组中的元素
            Console.WriteLine(j_array[0][1]);

            // 修改元素
            j_array[0][1] = 102;
            Console.WriteLine(j_array[0][1]);

            // 遍历数组
            for(int j = 0;  j < j_array.GetLength(0); j++)
            {
                for(int d = 0; d < j_array[j].Length; d++)
                {
                    Console.WriteLine(j_array[j][d] + " ");
                }
            }

            // 增加\删除\查找

            #endregion

            #region 结构体的使用
            // 使用结构体，定义变量
            // 要想在这里访问结构体中的变量，方法  需要在变量和方法前加上public
            Student stu;
            stu.name = "张三";
            stu.age = 18;
            stu.sex = true;
            stu.Study();

            // 使用结构体，使用构造函数初始化  需要用 new 关键字
            Student stu2 = new Student("李四", 20, false);
            stu2.Study();
            #endregion
        }
    }
}
